package server.parser;

import java.io.DataInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.Enumeration;
import java.util.Scanner;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;

import server.ConnectionHandler;
import server.datamodel.Cell;
import server.datamodel.CreepSmashGame;
import server.datamodel.Map;
import server.datamodel.User;
import server.datamodel.Tile;
import server.datamodel.Waypoint;

public class LoginParser implements Parser {
	
	private ConnectionHandler connectionHandler;
	
	private boolean loggedIn = false;
	
	public LoginParser(ConnectionHandler connectionHandler) {
		this.connectionHandler = connectionHandler;
	}
	
	public LoginParser(ConnectionHandler connectionHandler, boolean loggedIn) {
		this(connectionHandler);
		
		this.loggedIn = loggedIn;
	}

	@Override
	public void parseMessage(String message) {
		if(loggedIn) {
			lobbyScreen(message);
		} else {
			loginScreen(message);
		}
		 
		anyScreen(message);
		
		System.out.println("received: " + message);
	}
	
	private void loginScreen(String message) {
		if(message.toLowerCase().startsWith("login")) {
			String[] split = message.split(" ");
			
			if(split.length != 3) {
				connectionHandler.sendMessage("unknown user");
			} else {
				if(split[1].equals("x") || split[2].equals("x") || split[1].contains(" ")) {
					connectionHandler.sendMessage("unknown user");
				} else {
					connectionHandler.setCurrentUser(new User());
					connectionHandler.getCurrentUser().setNickname(split[1]);
					connectionHandler.getCurrentUser().setTeam(connectionHandler.getDatamodel().getDefaultTeam());
					connectionHandler.getDatamodel().addUser(connectionHandler.getCurrentUser());
					connectionHandler.sendMessage("USER NICK=" + split[1] + " EMAIL=your@mail.adress ROLE=SE");
					connectionHandler.sendMessage("TEAM NAME=TEAMH ID=1asd89a");
					connectionHandler.sendMessage("OK");
					loggedIn = true;
				}
			}
		}
	}
	
	private void lobbyScreen(String message) {
		/*
		 * LIST USERS
		 */
		if(message.toLowerCase().equals("list users")) {
			connectionHandler.aquire();
			
			for(User user: connectionHandler.getDatamodel().getUsers()) {
				if(user.getUserAssets() != null) {
					connectionHandler.sendAsynchronMessage("USER NAME=" + user.getNickname() + " TEAM=" + user.getTeam().getName() + " STATE=GAME");
				} else {
					connectionHandler.sendAsynchronMessage("USER NAME=" + user.getNickname() + " TEAM=" + user.getTeam().getName() + " STATE=LOBBY");
				}
			}
			
			connectionHandler.sendAsynchronMessage("OK");
			connectionHandler.release();
		}
		
		/*
		 * MSG
		 */
		if(message.toLowerCase().startsWith("msg")) {
			int index = message.indexOf(" ");
			message = message.substring(index + 1);
			
			index = message.indexOf(" ");
			String type = message.substring(0, index);
			message = message.substring(index + 1);
			
			System.out.println("type: " + type);
			
			if(type.equals("ALL")) {
				connectionHandler.getProvider().broadcast("MSG " + connectionHandler.getCurrentUser().getNickname() + " ALL " + message);
			} else if(type.equals("USER")) {
				index = message.indexOf(" ");
				String recipient = message.substring(0, index);
				message = message.substring(index + 1);
				
				for(ConnectionHandler c: connectionHandler.getProvider().getClients().keySet()) {
					if(connectionHandler.getCurrentUser() != null && connectionHandler.getCurrentUser().getNickname().equals(recipient)) {
						c.sendMessage("MSG " + connectionHandler.getCurrentUser().getNickname() + " YOU " + message);
					}
				}
			}
		}
		
		/*
		 * CREATE GAME
		 */
		if(message.toLowerCase().startsWith("create game")) {
			String[] split = message.split(" ");
			String name = split[2];
			int numPlayers = Integer.parseInt(split[4]);
			String mapName = split[6];
			
			createGame(name, numPlayers, mapName);
		}
		
		/*
		 * LIST GAMES
		 */
		if(message.toLowerCase().equals("list games")) {
			connectionHandler.aquire();
			
			for(CreepSmashGame g: connectionHandler.getDatamodel().getGames()) {
				String mapName = g.getTiles().values().iterator().next().getMap().getName();
				connectionHandler.sendAsynchronMessage("GAME NAME=" + g.getName() + " MAP " + "MAPNAME=" + mapName + " PLAYERS=" + g.getNumPlayers() + " STATUS=" + (g.isRunning() ? "RUNNING" : "JOINING") + " TESTGAME=" + g.isTestGame());
			}
			
			connectionHandler.sendAsynchronMessage("OK");
			connectionHandler.release();
		}
		
		/*
		 * JOIN GAME
		 */
		if(message.toLowerCase().startsWith("join game")) {
			String[] split = message.split(" ");
			
			if(split.length != 3) {
				connectionHandler.sendMessage("ERROR");
			} else {
				String gameName = split[2];
				CreepSmashGame g = null;
				
				for(CreepSmashGame game: connectionHandler.getDatamodel().getGames()) {
					if(game.getName().equals(gameName)) {
						g = game;
						break;
					}
				}
				
				if(g != null) {
					connectionHandler.setCurrentParser(new GameParser(connectionHandler, g));
				}
			}
		}
	}
	
	private void createGame(String name, int numPlayers, String mapName) {
		CreepSmashGame game = new CreepSmashGame();
		game.setName(name);
		game.setNumPlayers(numPlayers);
		game.setRunning(false);
		game.setTestGame(false);
		game.setSpeed(1.0);
		
		Map allMapsData = loadMap(mapName);
		
		for(int i = 0; i < numPlayers; i++) {
			Tile tile = new Tile();

			Map map = new Map();
			map.setName(mapName);
			
			fillMapWithData(map, allMapsData, i);
			
			tile.setMap(map);
			
			switch(i) {
			case 0:
				tile.setPosition("TOP_LEFT"); 
				break;
			case 1:
				tile.setPosition("TOP_RIGHT");
				break;
			case 2:
				tile.setPosition("BOTTOM_RIGHT");
				break;
			case 3:
				tile.setPosition("BOTTOM_LEFT");
				break;
			}
			
			game.addTile(tile);
		}
		
		connectionHandler.getDatamodel().addGame(game);
	}
	
	private void fillMapWithData(Map map, Map mapData, int rotation) {
		map.setBackground(mapData.getBackground());
		map.setCreatedBy(mapData.getCreatedBy());
		map.setVersion(mapData.getVersion());

			for(int i = 0; i < 16; i++) {
				for(int j = 0; j < 16; j++) {
					Cell c = new Cell();
					c.setX(i);
					c.setY(j);
					for(Cell c1 : mapData.getCells().values()) {
						if(c1.getX() == c.getX() && c1.getY() == c.getY()) {
							c.setType(c1.getType());
							break;
						} else {
							c.setType("TOWER");
						}
					}
					
					rotateCell(c, rotation);
					
					map.addCell(c);
				}
			}
			
			Waypoint prev = null;
			
			for(int i = 0; i < mapData.getWaypoints().values().size(); i++) {
				Waypoint w_i = null;
				
				for(Waypoint w : mapData.getWaypoints().values()) {
					if(w.getIndex() == i) {
						w_i = w;
					}
				}
				
				Waypoint w = new Waypoint();
				
				for(Cell c : map.getCells().values()) {
					if(c.getX() == w_i.getCell().getX() && c.getY() == w_i.getCell().getY()) {
						w.setCell(c);
						break;
					}
				}
				
				w.setIndex(i);
				
				if(i == 0) {
					map.setEnterWaypoint(w);
				} else if(i == mapData.getWaypoints().values().size() - 1) {
					map.setExitWaypoint(w);
				}
				w.setPrevious(prev);
				
				map.addWaypoint(w);
				prev = w;
			}
	}
	
	private void rotateCell(Cell c, int rotation) {
		for(int i = 0; i < rotation; i++) {
			int y = c.getY();
			c.setY(c.getX());
			
			c.setX(15 - y);
		}
	}

	private Map loadMap(String mapName) {
		Map data = new Map();
		
		ZipFile zipFile = null;
		try {
			zipFile = new ZipFile("maps/" + mapName + ".zip");
		} catch (IOException e) {
			e.printStackTrace();
		}

	    Enumeration<? extends ZipEntry> entries = zipFile.entries();

	    Outer: while(entries.hasMoreElements()){
	    	Waypoint prev = null;
	    	
	        ZipEntry entry = entries.nextElement();
	        if(entry.getName().matches(".*map")) {
	        	try {
					Scanner scanner = new Scanner(zipFile.getInputStream(entry));
					
					int index = 0;
					while(scanner.hasNext()) {
						String line = scanner.nextLine();
						
						if(!line.startsWith("#")) {
							if(line.contains(".jpg")) {
								data.setBackground(line.split("\\.")[0]);
							} else if(line.startsWith("VERSION: ")) {
								data.setVersion(Integer.parseInt(line.split(" ")[1]));
							} else if(line.startsWith("OWNER: ")) {
								data.setCreatedBy(line.split(" ")[1]);
							} else if(line.contains(";")) {
								String[] split = line.split(";");
								
								Cell cell = new Cell();
								cell.setX(Integer.parseInt(split[0].trim()));
								cell.setY(Integer.parseInt(split[1].trim()));
								cell.setType("NONE");
								
								data.addCell(cell);
							} else if(line.contains(",")) {
								String[] split = line.split(",");
								
								Cell cell = new Cell();
								cell.setX(Integer.parseInt(split[0].trim()));
								cell.setY(Integer.parseInt(split[1].trim()));
								cell.setType("WALKABLE");
								
								Waypoint wp = new Waypoint();
								wp.setCell(cell);
								
								if(prev == null) {
									data.setEnterWaypoint(wp);
								} else {
									wp.setPrevious(prev);	
								}
								
								prev = wp;
								
								data.addCell(cell);
								wp.setIndex(index);
								data.addWaypoint(wp);
								
								index++;
							}
						}
					}
					
					scanner.close();
					zipFile.close();
					break Outer;
				} catch (IOException e) {
					e.printStackTrace();
				}
	        }
	    }
	    
	    return data;
	}
	
	public void anyScreen(String message) {
		/*
		 * HELP
		 */
		if(message.toLowerCase().equals("help")){
			
		} 
		
		/*
		 * LIST MAPS
		 */
		if(message.equals("LIST MAPS")) {
			File file = new File("maps");
			
			if(file.exists()) {
				File[] children = file.listFiles();
				
				connectionHandler.aquire();
				for(File f: children) {
					connectionHandler.sendAsynchronMessage("MAP NAME=" + f.getName().substring(0, f.getName().length() - 4) + " CREATOR=NOBODY VERSION=1");
				}
				
				connectionHandler.sendAsynchronMessage("OK");
				connectionHandler.release();
			}
		}  
		
		/*
		 * DOWNLOAD MAP
		 */
		if(message.toLowerCase().startsWith("download map")) {
			String[] split = message.split(" ");
			
			if(split.length != 3) {
				connectionHandler.sendMessage("unknown command");
			} else {
				File file = new File("maps/" + split[2] + ".zip");
				byte[] data = new byte[(int)file.length()];
				
				DataInputStream input;
				try {
					input = new DataInputStream(new FileInputStream(file));
					
					input.readFully(data);
					
					input.close();
					
					connectionHandler.aquire();
					connectionHandler.sendAsynchronMessage("SENDING " + data.length + " BYTES");
					connectionHandler.getSocket().getOutputStream().write(data);
					connectionHandler.release();
					
				} catch (FileNotFoundException e) {
					e.printStackTrace();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
	}
}
